import fs from 'fs';
import path from 'path';

interface RedisOptions {
  host?: string;
  port?: number;
  filePath?: string;
}

interface RedisCommand {
  command: string;
  args: any[];
}

export class Redis {
  private options: RedisOptions;
  private data: { [key: string]: any };
  private multiMode: boolean;
  private multiCommands: RedisCommand[];

  constructor(options: RedisOptions = {}) {
    this.options = options;
    this.data = {};
    this.multiMode = false;
    this.multiCommands = [];

    console.log(this.options.filePath)

    if (this.options.filePath) {
      const directoryPath = path.dirname(this.options.filePath);
      if (!fs.existsSync(directoryPath)) {
        fs.mkdirSync(directoryPath, { recursive: true });
      }

      if (fs.existsSync(this.options.filePath)) {
        const fileData = fs.readFileSync(this.options.filePath, 'utf-8');
        this.data = JSON.parse(fileData);
      } else {
        fs.writeFileSync(this.options.filePath, JSON.stringify(this.data));
      }
    }
  }

  public set(key: string, value: string): Redis {
    if (this.multiMode) {
      this.multiCommands.push({ command: 'set', args: [key, value] });
    } else {
      this.data[key] = value;

      this.saveDataToFile(); // 数据发生变化时写入文件
    }
    return this;
  }

  public get(key: string): string | null {
    return key in this.data ? this.data[key] : null;
  }

  public del(key: string): Redis {
    if (this.multiMode) {
      this.multiCommands.push({ command: 'del', args: [key] });
    } else {
      delete this.data[key];

      this.saveDataToFile();
    }
    return this;
  }

  public expire(key: string, seconds: number): Redis {
    if (this.multiMode) {
      this.multiCommands.push({ command: 'expire', args: [key, seconds] });
    } else {
      if (key in this.data) {
        setTimeout(() => {
          delete this.data[key];

          this.saveDataToFile();
        }, seconds * 1000);
      }
    }
    return this;
  }

  public mget(...keys: string[]): Array<string | null> {
    return keys.map((key) => this.get(key));
  }

  public mset(...keyValues: string[]): Redis {
    if (keyValues.length % 2 !== 0) {
      throw new Error('Invalid number of arguments for MSET');
    }

    for (let i = 0; i < keyValues.length; i += 2) {
      const key = keyValues[i];
      const value = keyValues[i + 1];
      this.set(key, value);
    }
    return this;
  }

  public incr(key: string): number {
    if (key in this.data && typeof this.data[key] === 'number') {
      this.data[key]++;

      this.saveDataToFile();
      return this.data[key];
    } else {
      this.data[key] = 1;

      this.saveDataToFile();
      return 1;
    }
  }

  public decr(key: string): number {
    if (key in this.data && typeof this.data[key] === 'number') {
      this.data[key]--;

      this.saveDataToFile();
      return this.data[key];
    } else {
      this.data[key] = -1;

      this.saveDataToFile();
      return -1;
    }
  }

  public flushall(): Redis {
    if (this.multiMode) {
      this.multiCommands.push({ command: 'flushall', args: [] });
    } else {
      this.data = {};

      this.saveDataToFile();
    }
    return this;
  }

  public quit(): Redis {
    // 模拟退出操作
    return this;
  }

  public multi(): Redis {
    if (this.multiMode) {
      throw new Error('Already in MULTI mode');
    }
    this.multiMode = true;
    return this;
  }

  public setnx(key: string, value: string): Redis {
    if (this.get(key) === null) {
      this.set(key, value);
    }
    return this;
  }

  public exec(): any[] {
    if (!this.multiMode) {
      throw new Error('Not in MULTI mode');
    }

    const commands = [...this.multiCommands]; // 复制一份原始的 commands 数组
    this.multiCommands = []; // 清空原数组

    const results: any[] = [];
    for (const command of commands) {
      const result = this.executeCommand(command);
      results.push(result);
    }

    this.multiMode = false;

    return results;
  }

  private executeCommand(command: RedisCommand): any {
    const { command: cmd, args } = command;
    let result: any;

    switch (cmd) {
      case 'set':
        result = this.set(args[0], args[1]);
        break;
      case 'get':
        result = this.get(args[0]);
        break;
      case 'del':
        result = this.del(args[0]);
        break;
      case 'expire':
        result = this.expire(args[0], args[1]);
        break;
      case 'mget':
        result = this.mget(...args);
        break;
      case 'mset':
        result = this.mset(...args);
        break;
      case 'incr':
        result = this.incr(args[0]);
        break;
      case 'decr':
        result = this.decr(args[0]);
        break;
      case 'flushall':
        result = this.flushall();
        break;
      case 'quit':
        result = this.quit();
        break;
      case 'setnx':
        result = this.setnx(args[0], args[1]);
        break;
      // 处理其他命令...
      default:
        throw new Error(`Unknown command: ${cmd}`);
    }

    return result;
  }

  private saveDataToFile() {
    if (this.options.filePath) {
      fs.writeFileSync(this.options.filePath, JSON.stringify(this.data));
    }
  }
}
